package com.trip.gateway.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.data.geo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String, String> template;

    /**
     * 读取数据
     *
     * @param key
     * @return
     */
    public String get(final String key) {
        return template.opsForValue().get(key);
    }

    /**
     * 写入数据
     */
    public boolean set(final String key, String value) {
        boolean res = false;
        try {
            template.opsForValue().set(key, value);
            res = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    public long addSetKey(final String key,String value){
        Long size = null;
        try {
            size = template.opsForSet().add(key, value);
        } catch (Exception e) {
            return size;
        }
        return size;
    }

    public String getSetKey(final String key) {
        return template.opsForSet().pop(key);
    }
    /**
     * 根据key更新数据
     */
    public boolean update(final String key, String value) {
        boolean res = false;
        try {
            template.opsForValue().getAndSet(key, value);
            res = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 根据key删除数据
     */
    public boolean del(final String key) {
        boolean res = false;
        try {
            template.delete(key);
            res = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 是否存在key
     */
    public boolean hasKey(final String key) {
        boolean res = false;
        try {
            res = template.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 给指定的key设置存活时间
     * 默认为-1，表示永久不失效
     */
    public boolean setExpire(final String key, long seconds) {
        boolean res = false;
        try {
            if (0 < seconds) {
                res = template.expire(key, seconds, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 获取指定key的剩余存活时间
     * 默认为-1，表示永久不失效，-2表示该key不存在
     */
    public long getExpire(final String key) {
        long res = 0;
        try {
            res = template.getExpire(key, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 移除指定key的有效时间
     * 当key的有效时间为-1即永久不失效和当key不存在时返回false，否则返回true
     */
    public boolean persist(final String key) {
        boolean res = false;
        try {
            res = template.persist(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     *添加地理位置
     * @param key 司机,用户位置集合key
     * @param point 经纬度
     * @param personId id
     * @return
     */
    private boolean addGeo(String key , Point point , String personId){
        Long add = template.opsForGeo().add(key, point, personId);
        assert add != null;
        return add.intValue() == 1 ;
    }

    public boolean addGeo (String key , String longitude , String latitude , String personId){
        try{
            return addGeo(key,new Point(Double.parseDouble(longitude),Double.parseDouble(latitude)),personId);
        }catch (Exception e){
            throw new RuntimeException("redis addGeo error",e);
        }
    }

    /**
     * 获取两个地理位置之间的距离。
     *
     * @param key Redis 中存储地理数据的键。
     * @param src 起始地理位置的名称。
     * @param tar 目标地理位置的名称。
     * @param unit 距离单位，如公里、米等。
     * @return 两个地理位置之间的距离。
     */
    public double getDistance(String key, String src, String tar, RedisGeoCommands.DistanceUnit unit) {
        //获取距离
        Distance distance = template.opsForGeo().distance(key, src, tar, unit);
        if (distance != null) {
            return distance.getValue();
        } else {
            throw new IllegalArgumentException("Could not calculate distance between the specified locations.");
        }
    }

    /**
     * 获取指定点半径内的元素
     * @param key 集合key
     * @param longitude 经度
     * @param latitude 维度
     * @param radius 半径
     * @param unit 单位
     * @return
     */
    public List<String> getRadiusList(String key , String longitude , String latitude , int radius , RedisGeoCommands.DistanceUnit unit ){
        try{
            // 以经纬度为中心，获取半径不超过最大距离的所有元素
            Point point = new Point(Double.parseDouble(longitude),Double.parseDouble(latitude));
            // 半径为 radius unit
            Distance distance = new Distance(radius, unit);
            //⚪型
            Circle circle = new Circle(point, distance);
            //获取半径数据
            GeoResults<RedisGeoCommands.GeoLocation<String>> radiusList = template.opsForGeo().radius(key, circle);
            return getStrings(radiusList);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Invalid longitude or latitude format.", e);
        } catch (Exception e){
            throw new RuntimeException("get radiusList error",e);
        }
    }


    private List<String> getStrings(GeoResults<RedisGeoCommands.GeoLocation<String>> radiusList) {
        if (CollUtil.isEmpty(radiusList)) return Collections.emptyList();
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = radiusList.getContent();
        //提取value
        List<String> list = new ArrayList<>(content.size());
        content.forEach(geoLocationGeoResult -> {
            list.add(geoLocationGeoResult.getContent().getName());
        });
        return list;
    }


    /**
     * 获取点位的半径集合
     * @param key 集合key
     * @param centerPoint 中心点
     * @param radius 半径
     * @param unit 单位
     * @return
     */
    public List<String> getRadiusList(String key , String centerPoint , int radius , RedisGeoCommands.DistanceUnit unit){
        try{
            // 定义半径
            Distance distance = new Distance(radius, unit);
            // 查找为中心点半径radius unit的元素
            GeoResults<RedisGeoCommands.GeoLocation<String>> radiusList = template.opsForGeo().radius(key, centerPoint , distance);
            return getStrings(radiusList);
        }catch (Exception e){
            throw new RuntimeException("get radiusList error",e);
        }
    }

    /**
     * 获取点位经纬度
     * @param key 集合key
     * @param points 点集合
     * @return
     */
    public List<Point> getPointPosition(String key , String... points ){
        if (points == null || points.length == 0) {
            return Collections.emptyList(); // 如果没有点，返回空列表
        }
        try {
            return template.opsForGeo().position(key, points);
        } catch (Exception e) {
            throw new RuntimeException("get point position error", e);
        }
    }
    public Point getPoint(String key , String point){
        if(StrUtil.isBlank(point)) return null;
        List<Point> pointPosition = null;
        try{
             pointPosition = getPointPosition(key, point);
            if (CollUtil.isEmpty(pointPosition)) return null;
            return pointPosition.get(0);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}
